﻿using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml;

namespace PingSwitch
{
    public partial class frmPingSwitch : Form
    {
        static string currentFolder = Path.GetDirectoryName(Application.ExecutablePath);
        string hostList = "hostlist.txt";
        public bool listChanged = false;
        bool firstPing = true;
        int loopCount = 0;
        private static CancellationTokenSource tokenSource = new CancellationTokenSource();
        CancellationToken token = tokenSource.Token;
        ConcurrentBag<Task> tasks = new ConcurrentBag<Task>();
        ConcurrentDictionary<string, string> hostDictionary = new ConcurrentDictionary<string, string>();
        BindingSource bindingSource1 = new BindingSource();

        public frmPingSwitch()
        {
            InitializeComponent();
        }

        private void frmPingSwitch_Load(object sender, EventArgs e)
        {
            string file = currentFolder + "\\" + hostList;
            loadHostList(hostArray(file));
            initializeDataGrid();
            startParallelPingLoop();
        }

        private void initializeDataGrid()
        {
            //Dit zorgt ervoor dat er geen lege regel onderaan de datagrid komt
            dgPingList.AllowUserToAddRows = false;
            bindingSource1.DataSource = (from d in hostDictionary
                                     orderby d.Key
                                     select new { Hostname = d.Key, Result = d.Value }).ToList();

            dgPingList.DataSource = bindingSource1;
        }

        private void frmPingSwitch_FormClosing(object sender, FormClosingEventArgs e)
        {
            string file = currentFolder + "\\" + hostList;
            saveHostList(file);
        }

        private string[] hostArray(string file)
        {
            makeSureFileExists(file);
            string[] lines = null;
            try
            {
                lines = System.IO.File.ReadAllLines(file);
            }
            catch (Exception Ex)
            {
                MessageBox.Show(Ex.ToString());
            }
            return lines;
        }

        public void loadHostList(string[] hostArray)
        {
            string strippedLine = "";
            for (int i = 0; i < hostArray.Length; i++)
            {
                strippedLine = hostArray[i].Trim(new Char[] { ' ', '*', '.', ':', ';', ',' });
                if (strippedLine.Length > 0)
                {
                    hostDictionary.TryAdd(strippedLine, " - ");
                }
            }
            firstPing = true;
        }

        public void clearDataTable()
        {
            firstPing = true;
            hostDictionary.Clear();
        }

        private static IPAddress GetIpFromHost(ref string workstation)
        {
            //variable to hold our error message (if something fails)
            string errMessage = string.Empty;
            //IPAddress instance for holding the returned host
            IPAddress address = null;
            //wrap the attempt in a try..catch to capture
            //any exceptions that may occur
            try
            {
                //get the host IP from the name provided
                address = Dns.GetHostEntry(workstation).AddressList[0];
            }
            catch (System.Net.Sockets.SocketException ex)
            {
                //some DNS error happened, return the message
                errMessage = string.Format("DNS Error: {0}", ex.Message);
            }
            return address;
        }

        private static string PingHost(string strHost)
        {
            //string to hold our return messge
            IPStatus returnMessage = IPStatus.Unknown;
            //IPAddress instance for holding the returned host
            IPAddress address = null;
            // Find out if host is allready an ip address
            if (IPAddress.TryParse(strHost, out address))
            {
                address = IPAddress.Parse(strHost);
            }
            else
            {
                address = GetIpFromHost(ref strHost);
            }

            //set the ping options, TTL 128
            PingOptions pingOptions = new PingOptions(Properties.Settings.Default.TimeToLive, true);
            //create a new ping instance
            Ping ping = new Ping();
            //32 byte buffer (create empty)
            byte[] buffer = new byte[32];
            if (address != null)
            {
                try
                {
                    //send the ping 4 times to the host and record the returned data.
                    //The Send() method expects 4 items:
                    //1) The IPAddress we are pinging
                    //2) The timeout value
                    //3) A buffer (our byte array)
                    //4) PingOptions
                    PingReply pingReply = ping.Send(address, 1000, buffer, pingOptions);
                    //make sure we dont have a null reply
                    if (!(pingReply == null))
                    {
                        returnMessage = pingReply.Status;
                    }
                    else
                        returnMessage = pingReply.Status;
                }
                catch (PingException ex)
                {
                    returnMessage = IPStatus.Unknown;
                }
                catch (System.Net.Sockets.SocketException ex)
                {
                    returnMessage = IPStatus.Unknown;
                }
            }
            else
            {
                returnMessage = IPStatus.Unknown;
            }
            //return the message
            return returnMessage.ToString();
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        //Saves the currently monitored hosts to file.
        private void saveHostList(string hostlist)
        {
            try
            {
                using (System.IO.StreamWriter file = new System.IO.StreamWriter(hostlist))
                {
                    foreach (var host in hostDictionary)
                    {
                        file.WriteLine(host.Key.ToString());
                    }
                }
            }
            catch (Exception Ex)
            {
                MessageBox.Show(Ex.ToString());
            }
        }

        private void makeSureFileExists(string file)
        {
            if (!File.Exists(file))
            {
                try
                {
                    StreamWriter sw = File.CreateText(file);
                }
                catch (Exception Ex)
                {
                    MessageBox.Show(Ex.ToString());
                }
            }
        }

        private int hostCount()
        {
            int count = hostDictionary.Count;
            return count;
        }

        private int onlinehosts()
        {
            int online = hostDictionary.Values.Count(v => v == "Success");
            return online;
        }

        public void startParallelPingLoop()
        {
            Task t;
            t = Task.Factory.StartNew(() => ParallelPingLoop(token), token);
        }

        private void stopParallelPingLoop()
        {
            tokenSource.Cancel();
            try
            {
                Task.WaitAll(tasks.ToArray());
            }
            catch (AggregateException e)
            {
                MessageBox.Show(e.Message);
            }
        }

        private void ParallelPingLoop(CancellationToken ct)
        {
            string status = "Unknown";
            while (true)
            {
                System.Threading.Tasks.Parallel.ForEach(hostDictionary.AsEnumerable(), drow =>
                {
                    try
                    {
                        string host = drow.Key.ToString();
                        string previusStatus = drow.Value.ToString();

                        string message = "";
                        status = PingHost(host);
                        DateTime now = DateTime.Now;

                        if (status == "Success" && drow.Value.ToString() != "Success")
                        {
                            if (Properties.Settings.Default.AlertWhenUp && !firstPing)
                            {
                                message = drow.Key.ToString() + "#Is nu online (" + now.ToShortTimeString() + ")#green";
                            }
                        }
                        else if (drow.Value.ToString() == "Success")
                        {
                            if (Properties.Settings.Default.AlertWhenDown && !firstPing)
                            {
                                message = drow.Key.ToString() + "#Is nu offline (" + now.ToShortTimeString() + ")#red";
                            }
                        }
                        hostDictionary.TryUpdate(host, status, previusStatus);
                    }
                    catch (System.ArgumentOutOfRangeException e)
                    {
                        MessageBox.Show(e.Message);
                    }
                    //evaluateRowColors();
                    setStatusLabel(loopCount);
                });
                Task.WaitAll(tasks.ToArray());
                loopCount++;
                firstPing = false;
                System.Threading.Thread.Sleep(Properties.Settings.Default.PingInterval);
            }
        }

        private void evaluateRowColors()
        {
            foreach (DataGridViewRow row in dgPingList.Rows)
            {
                try
                {
                    if (row.Cells[1].Value.ToString() == "Success")
                    {
                        row.DefaultCellStyle.BackColor = Color.LightGreen;
                    }
                    else
                    {
                        row.DefaultCellStyle.BackColor = Color.Empty;
                    }
                }
                catch (System.IndexOutOfRangeException e)
                {
                    MessageBox.Show(e.Message);
                }
            }
        }

        private void setStatusLabel()
        {
            StatusLabelPingCount.Text = "Hosts: " + hostCount() + " Online: " + onlinehosts();
        }

        private void setStatusLabel(int loopCount)
        {
            StatusLabelPingCount.Text = "Hosts: " + hostCount() + " Online: " + onlinehosts() + " LoopCount: " + loopCount;
        }

        private void statusChanged(object sender, ProgressChangedEventArgs e)
        {
            string[] words = e.UserState.ToString().Split('#');
            // words[0] = hostname
            // words[1] = message
            // words[2] = color

            if (words.Count() == 3)
            {
                if (e.UserState.ToString().Length > 0)
                {
                    frmNotify notify = new frmNotify(e.UserState.ToString());
                    notify.Show();
                }
            }
        }

        private void copyToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // The context menu strip called this method, so we need to determine which DGV it was clicked on    
            ToolStripDropDownItem item = sender as ToolStripDropDownItem;
            if (item == null) // Error            
                return;
            ContextMenuStrip strip = item.Owner as ContextMenuStrip;
            DataGridView grid = strip.SourceControl as DataGridView;
            if (grid == null) // Control wasn't a DGV
                return;
            string selection = "";
            Int32 selectedRowCount =
                grid.Rows.GetRowCount(DataGridViewElementStates.Selected);
            if (selectedRowCount > 0)
            {
                for (int i = 0; i < selectedRowCount; i++)
                {
                    string hostName = grid.SelectedRows[i].Cells[0].Value.ToString();
                    selection += hostName + Environment.NewLine;
                }
            }

            // DataObject data = grid.GetClipboardContent();
            DataObject data = new DataObject();
            data.SetData(selection);
            if (data != null)
                Clipboard.SetDataObject(data);
        }

        private void deleteToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Int32 selectedRowCount =
                dgPingList.Rows.GetRowCount(DataGridViewElementStates.Selected);

            if (selectedRowCount > 0)
            {
                List<string> hostsToDelete = new List<string>();
                // Create a list of Hosts to delete
                for (int i = 0; i < selectedRowCount; i++)
                {
                    string hostName = dgPingList.SelectedRows[i].Cells[0].Value.ToString();
                    hostsToDelete.Add(hostName);
                }
                // Delete selected host entrys
                string dummy;
                foreach (string host in hostsToDelete)
                {
                    hostDictionary.TryRemove(host, out dummy);
                }
            }
        }

        private void exitToolStripMenuItem_Click_1(object sender, EventArgs e)
        {
            this.Close();
        }

        private void aboutToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            frmAbout about = new frmAbout();
            about.Show();
        }

        private void closeNotificationsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            for (int i = Application.OpenForms.Count - 1; i >= 0; i--)
            {
                if (Application.OpenForms[i].Name == "frmNotify")
                    Application.OpenForms[i].Close();
            }
        }

        private void addHostsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            stopParallelPingLoop();
            String[] strHosts = hostDictionary.Keys.ToArray();
            frmAddComputers addComputers = new frmAddComputers(this, strHosts);
            addComputers.Show();
        }

        private void settingsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            frmSettings settings = new frmSettings();
            settings.Show();
        }
    }
    }